<template>
    <div class="row item-start" style="align-items:stretch">


        <div class="shrink0 column" style="width: 420px; ">

            <div class="row item-center gap15 shrink0">
                <span class="form-header shrink0">角色</span>
                <n-select v-model:value="selectedModel" :options="optModels" @update:value="loadSelectedModel" />
                <n-button @click="openModelDir" title="打开目录">
                    <svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M895.8 592.1a32.2 32.1 0 1 0 64.4 0 32.2 32.1 0 1 0-64.4 0Z" fill="#515151" p-id="3555"></path><path d="M928 687.9c-17.8 0-32.2 14.4-32.2 32.1v80.7c0 35.3-28.7 64-64 64H192.5c-35.3 0-64-28.7-64-64V225c0-35.3 28.7-64 64-64H415l82.7 143.3c6.6 11.4 19.2 17.2 31.5 15.8h302.5c35.3 0 64 28.7 64 64v80c0 17.7 14.4 32.1 32.2 32.1s32.2-14.4 32.2-32.1c0-0.8 0-1.6-0.1-2.4v-77.5C960 329 925.1 282 876.1 264v-1c0-68.2-55.8-124-124-124H476.2l-14.8-25.7c-7.4-12.9-18-16.2-27.3-16l-0.1-0.1H192.1c-70.7 0-128 57.3-128 128v574.1c0 70.7 57.3 128 128 128h640c70.7 0 128-57.3 128-128v-76.9c0.1-0.8 0.1-1.6 0.1-2.4 0-17.7-14.4-32.1-32.2-32.1zM747.1 202.8c31.6 0 58 23.2 63.1 53.4H543.9l-30.8-53.4h234z" fill="#515151"></path></svg>
                </n-button>
                <n-button @click="loadModelList" title="重新加载所有角色">
                    <n-icon><Refresh /></n-icon>
                </n-button>
            </div>

            <div class="column grow">

                <Collapse title="背景">
                    <div class="grow row" style="flex-flow: wrap;">
                        <div v-for="bg,idx in optBgs" class="bg-outter column center" :class="{selected: selectedBgIdx==idx}" @click="selectBg(idx)">
                            <img :src="'file://'+bgdir+'/'+bg.filename" style="height:60px">
                        </div>
                    </div>
                </Collapse>
                <Collapse title="动画">
                    <div v-show="!selectedModel" style="margin:20px; color: #f18699">
                        请先选择角色
                    </div>
                    <div class="grow row" style="flex-flow: wrap;">
                        <n-button v-for="name of motionNames" style="margin-left:5px; margin-top:5px" @click="playAnimation(name)" >
                            {{name}}
                        </n-button>
                    </div>
                </Collapse>
                <Collapse title="语音">
                    <div class="row gap15" style="margin-top: 15px;">
                        <n-select v-model:value="selectedVoice" :options="optVoice" />
                        <n-button @click="openVoiceDir" title="打开目录">
                            <svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M895.8 592.1a32.2 32.1 0 1 0 64.4 0 32.2 32.1 0 1 0-64.4 0Z" fill="#515151" p-id="3555"></path><path d="M928 687.9c-17.8 0-32.2 14.4-32.2 32.1v80.7c0 35.3-28.7 64-64 64H192.5c-35.3 0-64-28.7-64-64V225c0-35.3 28.7-64 64-64H415l82.7 143.3c6.6 11.4 19.2 17.2 31.5 15.8h302.5c35.3 0 64 28.7 64 64v80c0 17.7 14.4 32.1 32.2 32.1s32.2-14.4 32.2-32.1c0-0.8 0-1.6-0.1-2.4v-77.5C960 329 925.1 282 876.1 264v-1c0-68.2-55.8-124-124-124H476.2l-14.8-25.7c-7.4-12.9-18-16.2-27.3-16l-0.1-0.1H192.1c-70.7 0-128 57.3-128 128v574.1c0 70.7 57.3 128 128 128h640c70.7 0 128-57.3 128-128v-76.9c0.1-0.8 0.1-1.6 0.1-2.4 0-17.7-14.4-32.1-32.2-32.1zM747.1 202.8c31.6 0 58 23.2 63.1 53.4H543.9l-30.8-53.4h234z" fill="#515151"></path></svg>
                        </n-button>
                        <n-button>
                            <svg viewBox="0 0 1024 1024" width="16" height="16"><path d="M224 938.713333a53.58 53.58 0 0 1-53.333333-53.433333V138.72a53.333333 53.333333 0 0 1 80.886666-45.666667l618.666667 373.28a53.333333 53.333333 0 0 1 0 91.333334l-618.666667 373.28a53.16 53.16 0 0 1-27.553333 7.766666z m0.046667-810.666666a10.98 10.98 0 0 0-5.333334 1.42 10.466667 10.466667 0 0 0-5.38 9.253333v746.56a10.666667 10.666667 0 0 0 16.18 9.133333l618.666667-373.28a10.666667 10.666667 0 0 0 0-18.266666l-618.666667-373.28a10.386667 10.386667 0 0 0-5.446666-1.586667z" fill="#5C5C66"></path></svg>
                        </n-button>
                    </div>
                    <div style="margin-top: 10px;">
                        <n-checkbox v-model:checked="bPlayVoiceWithAnimation" size="large" label="随动画播放" />
                    </div>
                </Collapse>
                <Collapse title="调试">
                </Collapse>

            </div>

        </div>


        <div class="grow" style="margin-left:20px; width:320px">
            <div class="row center" style="position: relative;">
                <canvas ref="refEncodeCanvas" style="display:none; border-style:solid; border-width:1px;"></canvas>
                <div ref="refCanvasOuter" :style="{height:stageHeight+'px',width:stageWidth+'px'}"></div>
                <div v-show="editing" ref="refEditMask" style="position:absolute;border: solid 1px black;" :style="{height:stageHeight+'px',width:stageWidth+'px'}"></div>
            </div>
            <div class="row end item-end" style="margin-top:10px">
                <span v-if="editing" class="grow">
                    鼠标滚轮缩放，左键拖动位置
                </span>
                <n-button v-show="!editing" @click="startEdit">编辑</n-button>
                <n-button v-show="editing" @click="endEdit" type="error">完成</n-button>
            </div>
        </div>

    </div>
</template>

<style scoped>
.form-header {
    width:50px ;
}

.bg-outter {
    padding: 5px;
    height: 70px;
}
.bg-outter.selected {
    border: 2px solid #059e2d;
    padding: 3px;
}
</style>

<script setup lang="ts">
import { ref, onMounted, Ref, inject, reactive  } from 'vue'
import {Viewer} from './live2d/live2d.js'
import {FrameBuffer,base64Decode} from './FrameBuffer'
import { CaptureWebGL } from './Capture';
const fs = (window as any).require("fs")
const path = (window as any).require("path")
const gui = (window as any).require('nw.gui')
import { Refresh } from '@vicons/tabler'

const quality = inject("quality") as Ref<number>

const frameBuffer = inject<FrameBuffer>("frameBuffer") as FrameBuffer

const editing = ref(false)
let refEditMask = ref(null) as unknown as Ref<HTMLDivElement>
let dragging = false
let dragStartAt = {
    ox:0, oy:0
}

const motionNames: Ref<string[]> = ref([])

const stageWidth = inject("stageWidth") as Ref<number>
const stageHeight = inject("stageHeight") as Ref<number>


const appdir = path.dirname((window as any).process.mainModule.filename)
const modeldir = appdir+'/assets/live2d/model'
const bgdir = appdir+'/assets/live2d/bg'
const voicedir = appdir+'/assets/live2d/voice'


const selectedModel = ref('')
const optModels: Ref<{label: string, value: string}[]> = ref([])

const selectedBgIdx = ref(-1)
const optBgs: Ref<any[]> = ref([])

const selectedVoice = ref('')
const optVoice: Ref<{label: string, value: string}[]> = ref([])
const bPlayVoiceWithAnimation = ref(false)


const refCanvasOuter = ref(null) as unknown as Ref<HTMLDivElement>
const live2d = new Viewer(modeldir)

const refEncodeCanvas = ref(null) as unknown as Ref<HTMLCanvasElement>
let ctx: CanvasRenderingContext2D | null = null
let gl: WebGLRenderingContext  | null = null

const capture = reactive(new CaptureWebGL('live2d', frameBuffer))


function findModelJson(dirname:string) {
    let path = modeldir+"/"+dirname+"/"+dirname+'.model3.json'
    if(fs.existsSync(path)) {
        return path
    }
    return null

}

function loadModelList() {
    optModels.value.length = 0

    let dirs = fs.readdirSync(modeldir)
    for(let dirname of dirs) {
        let jsonPath = findModelJson(dirname)
        if(!jsonPath) {
            continue
        }
        let json 
        try{
            json = window.require(jsonPath)
        }catch(e){}

        optModels.value.push({
            label: json?.name || dirname ,
            value: dirname
        })
    }
}

function loadBg() {
    optBgs.value.length = 0
    let dirs = fs.readdirSync(bgdir)
    for(let filename of dirs) {
        let extname = (filename.split('.').pop()||'').toLowerCase()
        if(extname!='png'&&extname!='jpg'&&extname!='jpeg'){
            continue
        }
        optBgs.value.push({filename,sprite:null})
    }
}

function loadVoice() {
    optVoice.value.length = 0
    let dirs = fs.readdirSync(voicedir)
    for(let filename of dirs) {
        let arr = filename.split('.')
        let extname = (arr.pop()||'').toLowerCase()
        if(extname!='ogg'&&extname!='wav'&&extname!='mp3'){
            continue
        }
        optVoice.value.push({label:arr.join('.'),value:filename})
    }
}

function selectBg(idx) {
    selectedBgIdx.value = idx

    let bgobj = optBgs.value[idx]
    if(bgobj.sprite) {
        live2d.setBg(bgobj.sprite)
        frameBuffer.needKeyframe = true
    } else {
        let path = bgdir+'/'+bgobj.filename
        let t = (window as any).PIXI.Texture.from(path)
        t.on('update', ()=>{
            bgobj.sprite = new (window as any).PIXI.Sprite(t)
            live2d.setBg(bgobj.sprite)
            frameBuffer.needKeyframe = true
        })
    }
}


onMounted(()=>{
    console.log("live2d onMounted()")
    live2d.app.view.id = "canvasLive2d"
    ; (window as any).live2d = live2d
    ; (window as any).pixi = live2d.app
    refCanvasOuter.value?.appendChild(live2d.app.view)
    loadModelList()
    loadBg()
    loadVoice()
    
    gl = live2d.app.view.getContext("webgl") 
    ctx = refEncodeCanvas.value.getContext("2d")

    refEditMask.value.addEventListener("mousewheel", (e:any)=>{
        if(e.deltaY>0) {
            live2d.model.scale.x-=2
            live2d.model.scale.y-=2
        }
        else {
            live2d.model.scale.x+=2
            live2d.model.scale.y+=2
        }
    }, false)
    refEditMask.value.addEventListener("mousedown", (e:any)=>{
        dragging = true
        dragStartAt.ox = live2d.model.x - e.clientX
        dragStartAt.oy = live2d.model.y - e.clientY
    },false)
    refEditMask.value.addEventListener("mousemove", (e:any)=>{
        if(!dragging){
            return
        }
        live2d.model.x = e.clientX + dragStartAt.ox
        live2d.model.y = e.clientY + dragStartAt.oy

    },false)
    refEditMask.value.addEventListener("mouseup", (e:any)=>{
        dragging = false
    },false)
    refEditMask.value.addEventListener("mouseout", (e:any)=>{
        dragging = false
    },false)
})

function loadSelectedModel() {
    // console.log(selectedModel.value)

    live2d.load(selectedModel.value, model=>{
        live2d.changeCanvas(model)

        motionNames.value.length = 0
        model.motions.forEach((value, key) => {
            if(key!='effect') {
                motionNames.value.push(key)
            }
        })

        // 自动调整过大的模型
        let stage: any = {width:stageWidth.value, height: stageHeight.value}
        let compareDimen = (model.width/model.height > stageWidth.value/stageHeight.value)? 'width': 'height'
        if( model[compareDimen]>stage[compareDimen]*2 ) {
            let scale = (stage[compareDimen]*2)/(model[compareDimen])
            model.scale.x*= scale
            model.scale.y*= scale
        }

    })
}

function startEdit() {
    editing.value = true
}

function endEdit() {
    editing.value = false
}

function openModelDir(){
    gui.Shell.openItem(modeldir)
}
function openVoiceDir(){
    gui.Shell.openItem(voicedir)
}

let lastRect: any = null

live2d.onFrame = function() {
    if(frameBuffer.savingFrames) {
        frameBuffer.saveOneFrame(live2d.app.view.toDataURL("image/jpeg", 100))
    }
    if(!ctx) {
        return
    }
    if(frameBuffer.isFull()) {
        return
    }
    if(!frameBuffer.acceptTest('live2d')) {
        return
    }
    let bounds = live2d.model.getBounds()
    let thisRect = {
        x1: Math.max(0, bounds.x) ,
        y1: Math.max(0, bounds.y) ,
        x2: Math.min(stageWidth.value, bounds.x + bounds.width-1) ,
        y2: Math.min(stageHeight.value, bounds.y + bounds.height-1) ,
    }
    thisRect.x1 = Math.floor(thisRect.x1)
    thisRect.y1 = Math.floor(thisRect.y1)
    thisRect.x2 = Math.ceil(thisRect.x2)
    thisRect.y2 = Math.ceil(thisRect.y2)

    if(frameBuffer.needKeyframe) {
        lastRect = null
    }

    if(lastRect) {
        var rect = {
            x1: Math.min(thisRect.x1, lastRect.x1) ,
            y1: Math.min(thisRect.y1, lastRect.y1) ,
            x2: Math.max(thisRect.x2, lastRect.x2) ,
            y2: Math.max(thisRect.y2, lastRect.y2) ,
        }

        // rect.x1 = Math.max(rect.x1,0)
        // rect.y1 = Math.max(rect.y1,0)
        // rect.x2 = Math.min(rect.x2+40,stageWidth.value)
        // rect.y2 = Math.min(rect.y2+40,stageHeight.value)

        var width = rect.x2 - rect.x1
        var height = rect.y2 - rect.y1
    }
    else {
        var width = stageWidth.value
        var height = stageHeight.value
        var rect = {
            x1:0,y1:0,
            x2:width-1,
            y2:height-1
        }
    }

    refEncodeCanvas.value.width = width
    refEncodeCanvas.value.height = height
    refEncodeCanvas.value.style.width = width + 'px'
    refEncodeCanvas.value.style.height = height + 'px'

    ctx.drawImage(gl?.canvas as HTMLCanvasElement, rect.x1, rect.y1, width, height, 0,0,width,height);
    
    let urlData = refEncodeCanvas.value.toDataURL("image/jpeg", quality.value/100)

    if(frameBuffer.pushVideoFrameBase64('live2d',urlData,rect.x1,rect.y1)) {
        lastRect = thisRect
        frameBuffer.needKeyframe = false
    }
}

function playAnimation(name) {
    live2d.startAnimation(name,'base')
}

function getCapture() {
    return capture
}

async function onenter() {
    capture.setStageSize(stageWidth.value, stageHeight.value)
    live2d.resize(stageWidth.value, stageHeight.value)
}

defineExpose({getCapture, onenter})
</script>